package com.sjtu.vfact.graph.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Paint;
import java.awt.geom.Point2D;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JPanel;
import javax.swing.JRootPane;

import org.apache.commons.collections15.functors.ConstantTransformer;
import org.apache.commons.collections15.functors.MapTransformer;
import org.apache.commons.collections15.map.LazyMap;

import com.sjtu.vfact.base.VfactEdge;
import com.sjtu.vfact.base.VfactVertex;
import com.sjtu.vfact.textual.model.Cluster;
import com.sjtu.vfact.textual.model.Document;
import com.sjtu.vfact.textual.model.MasterTermset;
import com.sjtu.vfact.textual.model.TermSet;

import edu.uci.ics.jung.algorithms.layout.AbstractLayout;
import edu.uci.ics.jung.algorithms.layout.AggregateLayout;
import edu.uci.ics.jung.algorithms.layout.CircleLayout;
import edu.uci.ics.jung.algorithms.layout.FRLayout2;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.util.Relaxer;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.ObservableGraph;
import edu.uci.ics.jung.graph.SparseMultigraph;
import edu.uci.ics.jung.graph.event.GraphEvent;
import edu.uci.ics.jung.graph.event.GraphEventListener;
import edu.uci.ics.jung.graph.util.EdgeType;
import edu.uci.ics.jung.graph.util.Graphs;
import edu.uci.ics.jung.graph.util.Pair;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;

public class GraphPanel{
	
	private Graph<VfactVertex, VfactEdge> graph;
	private VisualizationViewer<VfactVertex, VfactEdge> viewer;
//	private AbstractLayout<VfactVertex, VfactEdge> layout;
	private AggregateLayout<VfactVertex, VfactEdge> layout;
	private GraphZoomScrollPane gzsp;
	private List<MasterTermset> list;
	private Timer timer;
	private boolean showGraphDone, clusterDone, animation;
	int totalVertices = 0;
	int vertexIdx;
	
	Map<VfactVertex,Paint> vertexPaints = LazyMap.<VfactVertex,Paint>decorate(new HashMap<VfactVertex,Paint>(),
			new ConstantTransformer(Color.WHITE));
	
	public final Color[] colors = {new Color(216, 134, 134), new Color(135, 137, 211), new Color(134, 206, 189),
				new Color(206, 176, 134), new Color(194, 204, 134), new Color(145, 214, 134),
				new Color(133, 178, 209), new Color(103, 148, 255), new Color(60, 220, 220),
				new Color(30, 250, 100)
	};
	
	public void setupView(List<MasterTermset> list){
//		MasterTermset mts0 = new MasterTermset(0, new TermSet(new String[]{"Term0", "Term2"}));
//		MasterTermset mts1 = new MasterTermset(1, new TermSet(new String[]{"Term1", "Term3"}));
//		MasterTermset mts2 = new MasterTermset(2, new TermSet(new String[]{"Term2", "Term4"}));
//		MasterTermset mts3 = new MasterTermset(3, new TermSet(new String[]{"Term3", "Term5"}));
//		MasterTermset mts4 = new MasterTermset(4, new TermSet(new String[]{"Term4", "Term6"}));
//		this.list = new ArrayList<MasterTermset>();
//		this.list.add(mts0);
//		this.list.add(mts1);
//		this.list.add(mts2);
//		this.list.add(mts3);
//		this.list.add(mts4);
		this.list = list;
		graph = new DirectedSparseMultigraph<VfactVertex, VfactEdge>();
//		ObservableGraph<VfactVertex, VfactEdge> og = new ObservableGraph<VfactVertex, VfactEdge>(sg);
//		og.addGraphEventListener(new GraphEventListener<VfactVertex, VfactEdge>() {
//			
//			@Override
//			public void handleGraphEvent(GraphEvent<VfactVertex, VfactEdge> evt) {
//				System.err.println("got " + evt);
//			}
//		});
		
//		graph = sg;
		
//		for(MasterTermset vertex : list){
//			graph.addVertex(vertex.getTermset());
//		}
		
		layout = new AggregateLayout<VfactVertex, VfactEdge>(new FRLayout<VfactVertex, VfactEdge>(graph));
		
		viewer = new VisualizationViewer<VfactVertex, VfactEdge>(layout);
		viewer.setBackground(Color.WHITE);
		viewer.getRenderContext().setVertexFillPaintTransformer(MapTransformer.<VfactVertex,Paint>getInstance(vertexPaints));
		viewer.setVertexToolTipTransformer(new ToStringLabeller<VfactVertex>());
		viewer.getModel().getRelaxer().setSleepTime(500);
		final DefaultModalGraphMouse<VfactVertex, String> graphMouse = new DefaultModalGraphMouse<VfactVertex, String>();
		viewer.setGraphMouse(graphMouse);
		
		vertexIdx = 0;
		timer = new Timer();
		timer.schedule(new ShowGraphTask(), 1000, 1000);
		viewer.repaint();
		
		gzsp = new GraphZoomScrollPane(viewer);
		
	}
	
	public void process(){
		viewer.getRenderContext().getPickedVertexState().clear();
		viewer.getRenderContext().getPickedEdgeState().clear();
		try{
			if(vertexIdx < this.list.size()){
				for(VfactVertex v : graph.getVertices()){
					layout.lock(v, true);
				}
				Relaxer relaxer = viewer.getModel().getRelaxer();
				relaxer.pause();
				VfactVertex vertex = this.list.get(vertexIdx).getTermset();
				graph.addVertex(vertex);
				colorVertex(vertex, Color.RED);
				System.err.println("added node : " + vertex);
				
				layout.initialize();
				relaxer.resume();
				for(VfactVertex v : graph.getVertices()){
					layout.lock(v, false);
				}
				vertexIdx++;
			}else{
				showGraphDone = true;
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	class ShowGraphTask extends TimerTask{

		@Override
		public void run() {
			process();
			if(showGraphDone) cancel();
		}
		
	}
	
	public void setupSSAView(Map<TermSet, Set<Document>> ssaDict){
		graph = new DirectedSparseMultigraph<VfactVertex, VfactEdge>();
		
		int edgeId = 1;
		for(Map.Entry<TermSet, Set<Document>> entry : ssaDict.entrySet()){
			TermSet key = entry.getKey();
			Set<Document> value = entry.getValue();
			graph.addVertex(key);
			colorVertex(key, colors[1]);
			for(Document doc : value){
				graph.addVertex(doc);
				colorVertex(doc, colors[2]);
				graph.addEdge(new VfactEdge(edgeId, "Edge-" + edgeId), doc, key, EdgeType.DIRECTED);
			}
		}
		
		layout = new AggregateLayout<VfactVertex, VfactEdge>(new CircleLayout<VfactVertex, VfactEdge>(graph));
		
		viewer = new VisualizationViewer<VfactVertex, VfactEdge>(layout);
		viewer.setBackground(Color.WHITE);
		viewer.getRenderContext().setVertexFillPaintTransformer(MapTransformer.<VfactVertex,Paint>getInstance(vertexPaints));
		
		viewer.setVertexToolTipTransformer(new ToStringLabeller<VfactVertex>());
		
		final DefaultModalGraphMouse<VfactVertex, String> graphMouse = new DefaultModalGraphMouse<VfactVertex, String>();
		viewer.setGraphMouse(graphMouse);
		
		gzsp = new GraphZoomScrollPane(viewer);
	}
	
	public GraphZoomScrollPane getGZSP(){
		return this.gzsp;
	}
	
	public Graph<VfactVertex, VfactEdge> getGraph(){
		return graph;
	}
	
	public void unclusterGraph(){
		layout.removeAll();
		viewer.setGraphLayout(layout);
		colorVertices(graph.getVertices(), Color.RED);
	}
	
	class ClusterTask extends TimerTask{
		
		private Cluster<VfactVertex> c;
		ClusterTask(Cluster c){
			this.c = c;
		}
		
		@Override
		public void run() {
			paintCluster(c);
			if(showGraphDone) cancel();
		}
		
	}
	
	public void clusterGraph(List<Cluster<VfactVertex>> clusters){
		int clusterIdx = 0;
//		timer = new Timer();
//		timer.schedule(new ClusterTask(), 1000, 1000);
		for(Cluster<VfactVertex> c : clusters){
			
			paintCluster(c);
			Color color = colors[clusterIdx % colors.length];
			colorVertices(c.getAllVertices(), color);
			clusterIdx++;
			
		}
	}
	
	private void paintCluster(Cluster<VfactVertex> cluster){
		Collection<VfactVertex> vertices = cluster.getAllVertices();
		Point2D center = new Point2D.Double();
		double x = 0.0;
		double y = 0.0;
		for(VfactVertex vertex : vertices){
			Point2D p = layout.transform(vertex);
			x += p.getX();
			y += p.getY();
		}
		
		x /= cluster.getSize();
		y /= cluster.getSize();
		center.setLocation(x, y);
		
		Graph<VfactVertex, VfactEdge> subgraph;
		try {
			subgraph = graph.getClass().newInstance();
			for(VfactVertex vertex : vertices){
				subgraph.addVertex(vertex);
				Collection<VfactEdge> incidentEdges = graph.getIncidentEdges(vertex);
				for(VfactEdge edge : incidentEdges){
					Pair<VfactVertex> endpoints = graph.getEndpoints(edge);
					if(vertices.containsAll(endpoints)){
						subgraph.addEdge(edge, endpoints.getFirst(), endpoints.getSecond());
					}
				}
			}
			
//			Layout<VfactVertex, VfactEdge> subLayout = new CircleLayout<VfactVertex, VfactEdge>(subgraph);
			Layout<VfactVertex, VfactEdge> subLayout = getLayoutFor(FRLayout.class, subgraph);
			subLayout.setInitializer(viewer.getGraphLayout());
			subLayout.setSize(new Dimension(75, 75));
			layout.put(subLayout, center);
			viewer.setGraphLayout(layout);
			
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
	
	private Layout getLayoutFor(Class layoutClass, Graph graph) throws Exception {
    	Object[] args = new Object[]{graph};
    	Constructor constructor = layoutClass.getConstructor(new Class[] {Graph.class});
    	return  (Layout)constructor.newInstance(args);
    }
	
	private void colorVertices(Collection<VfactVertex> vertices, Color color){
		for(VfactVertex vertex : vertices){
			vertexPaints.put(vertex, color);
		}
		
	}
	
	private void colorVertex(VfactVertex vertex, Color color){
		vertexPaints.put(vertex, color);
	}
	
//	private void markPrototype(){
//		vv.getRenderContext().setVertexDrawPaintTransformer(new Transformer<Number,Paint>() {
//			public Paint transform(Number v) {
//				if(vv.getPickedVertexState().isPicked(v)) {
//					return Color.cyan;
//				} else {
//					return Color.BLACK;
//				}
//			}
//		});
//
//	}
	
}
